package com.geek.gkpush.biz.job;

import com.dangdang.ddframe.job.api.ShardingContext;
import com.geek.gkpush.dal.bean.RetryMsgDO;
import com.geek.gkpush.dal.mapper.RetryMsgMapper;
import com.geek.gkpush.dal.model.RetryMsgScanModel;
import com.geek.gkpush.framework.job.AbstractLockSimpleJob;
import com.geek.gkpush.framework.retry.RetryMsgDispatch;
import com.geek.gkpush.framework.retry.enums.ExecStatusEnum;
import com.geek.gkpush.framework.retry.model.RetryDelayModel;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;

/**
 * <h1>事件消息调度任务。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 *         ^_^
 * @version 0.0.1
 *         ^_^
 * @date 2023/10/2
 *
 */
@Component
public class RetryMsgJob extends AbstractLockSimpleJob {

    @Autowired
    private RetryMsgMapper retryMsgMapper;
    @Autowired
    private RetryMsgDispatch retryMsgDispatch;

    @Override
    public void doExecute(ShardingContext ctx) {
        // 构建重试扫描模型对象
        RetryMsgScanModel model = buildRetryMsgScanModel(ctx);

        // 翻页查询 db 数据，然后尝试添加到内存队列
        int currPageNo = 0;
        while (++currPageNo <= model.getMaxPageNo()) {
            // 1. 查询第 currPageNo 页数据
            PageHelper.startPage(currPageNo, model.getPageSize(), false);
            List<RetryMsgDO> dbRetryMsgList = retryMsgMapper.queryShardingListByRangeCreateTime(model);
            if (CollectionUtils.isEmpty(dbRetryMsgList)) {
                break;
            }

            // 2. 查出来的，如果小于 pageSize 个数的话，那就结束流程
            if (dbRetryMsgList.size() < model.getPageSize()) {
                processRetryMsgList(dbRetryMsgList);
                break;
            }

            // 3. 刚好满一页的话，那就处理数据
            processRetryMsgList(dbRetryMsgList);
        }
    }

    private void processRetryMsgList(List<RetryMsgDO> dbRetryMsgList) {
        for (RetryMsgDO dbRetryMsg : dbRetryMsgList) {
            RetryDelayModel delayModel = buildRetryDelayModel(dbRetryMsg);
            retryMsgDispatch.reAddRetryMsg(delayModel);
        }
    }

    private RetryMsgScanModel buildRetryMsgScanModel(ShardingContext ctx) {
        RetryMsgScanModel model = new RetryMsgScanModel();

        model.setStatusList(Lists.newArrayList(ExecStatusEnum.FAIL.getCode()));

        model.setShardingTotalCount(ctx.getShardingTotalCount());
        model.setShardingItemNo(ctx.getShardingItem());

        Date currDate = new Date();
        model.setBeginTime(DateUtils.addMinutes(currDate, -60));
        model.setEndTime(DateUtils.addMinutes(currDate, 5));

        model.setPageSize(500);
        model.setMaxPageNo(100);

        return model;
    }

    private RetryDelayModel buildRetryDelayModel(RetryMsgDO dbRetryMsg) {
        RetryDelayModel delayModel = new RetryDelayModel();

        delayModel.setId(dbRetryMsg.getId());
        delayModel.setBizId(delayModel.getBizId());

        delayModel.setBizValue(dbRetryMsg.getBizValue());
        delayModel.setBizType(dbRetryMsg.getBizType());

        delayModel.setExecTimes(dbRetryMsg.getExecTimes());
        delayModel.setMaxRetryTimes(dbRetryMsg.getMaxRetryTimes());
        delayModel.setExecStatus(dbRetryMsg.getExecStatus());
        delayModel.setExecTimeMillis(dbRetryMsg.getNextExecTime().getTime());

        return delayModel;
    }
}